import sys
import os
import time
import subprocess
from random import randint
from portalocker import lock, unlock, LOCK_EX, LOCK_SH, LOCK_NB, LockException

#
## Uncomment to see if the test fails without locks
#
##def dummy(*args): pass
#lock = dummy
#unlock = dummy

FILE_LEN=1000000

# This file makes a simple test on the lock API.
# It is used to check that the lock API is working on the target
# network filesystem.

def read_slowly(f, n):
    read = ''
    while len(read) < n:
        s = f.read(1)
        read += s
    return read

def write_slowly(f, s):
    written = 0
    while written < len(s):
        f.write(s[written:written+1])
        written += 1
        f.flush()

def do_setup(name):
    print 'setting up', name
    f = open(name, 'w')
    f.write('0'*FILE_LEN)
    f.close()
    return 0

def do_read(name):
    f = open(name, 'r')
    try:
        lock(f, LOCK_SH|LOCK_NB)
    except LockException, e:
        return 0
    except Exception, e:
        print 'Caught exception: %s, %s' % (type(e), str(e))
        return 1
    data1 = read_slowly(f, 8)
    f.seek(FILE_LEN-8)
    data2 = read_slowly(f, 8)
    unlock(f)
    f.close()
    if data1 != data2:
        print '%d: data1=%s, data2=%s'% (os.getpid(), data1, data2)
    return data1 != data2

def do_write(name):
    data = '%08x' % randint(0, 0xffffffff)
    f = open(name, 'w')
    try:
        lock(f, LOCK_EX)
    except LockException, e:
        return 0
    except Exception, e:
        print 'Caught exception: %s, %s' % (type(e), str(e))
        return 1
    write_slowly(f, data)
    f.seek(FILE_LEN-8)
    write_slowly(f, data)
    unlock(f)
    f.close()
    
class Stop:
    def __init__(self, name):
        self.name = name + '.stop'

    def check(self):
        try:
            os.stat(self.name)
            return 1
        except:
            return 0

    def set(self):
        open(self.name, 'w')

    def unset(self):
        try:
            os.unlink(self.name)
        except:
            pass


def do_run(filename, verbose=0):
    stop = Stop(filename)
    n = 1
    ret = 0
    while 1:
        if stop.check():
            break
        if randint(0, 1):
            if do_read(filename):
                print '%d: found a mismatch' % os.getpid()
                stop.set()
                ret = 1
                break
        else:
            do_write(filename)
        if verbose:
            print '%d: ok n.%d' % (os.getpid(), n)
        n += 1
    return ret

def do_test(filename, nprocesses, nseconds):
    prcs = []
    n = 1
    stop = Stop(filename)
    stop.unset()
    do_setup(filename)
    for n in xrange(nprocesses):
        print 'starting process %d' % n
        n += 1
        prcs.append(subprocess.Popen(['/usr/bin/python', 'test_lock.py', 'run', 'out', '0'], stdout=sys.stdout))
    for n in xrange(nseconds):
        for p in prcs:
            ret = p.poll()
            if ret == 1:
                print 'TEST FAILURE'
                sys.exit(1)
        print '%d: ok' % n
        time.sleep(1)
    stop.set()
    return 0

if __name__=='__main__':
    # python tlock.py run <filename> <verbose>
    # ex. python tlock.py run ./out.temp  1
    if sys.argv[1] == 'run':
        if len(sys.argv)==4:
            verbose=int(sys.argv[3])
        else:
            verbose = 0
        ret = do_run(sys.argv[2], verbose)
    # python tlock.py test <filename> <number of child processes> <seconds to run>
    # ex. python tlock.py test ./out.temp  5 60
    elif sys.argv[1] == 'test':
        ret = do_test(sys.argv[2], int(sys.argv[3]), int(sys.argv[4]))
    else:
        print 'wrong arguments, read the code'
        ret = 1
    sys.exit(ret)




